msg_tool\scripts\yuris/
yslb.rs1use crate::ext::io::*;
3use crate::scripts::base::*;
4use crate::types::*;
5use crate::utils::encoding::*;
6use crate::utils::struct_pack::*;
7use anyhow::Result;
8use msg_tool_macro::*;
9use serde::{Deserialize, Serialize};
10use std::io::{Read, Seek, Write};
11
12#[derive(Debug, StructUnpack, StructPack, Deserialize, Serialize)]
13pub(super) struct Label {
14 #[pstring(u8)]
15 pub name: String,
16 pub id: u32,
17 pub offset: u32,
18 pub script_index: u16,
19 #[serde(skip)]
20 padding: u16,
21}
22
23#[derive(Debug, StructUnpack, StructPack, Deserialize, Serialize)]
24pub(super) struct YSLBData {
25 pub version: u32,
26 #[serde(skip)]
27 num_labels: u32,
28 #[fvec = 0x100]
29 #[serde(skip)]
30 label_range_start_indexes: Vec<u32>,
32 #[pack_vec_len(self.num_labels)]
33 #[unpack_vec_len(num_labels)]
34 pub labels: Vec<Label>,
35}
36
37#[derive(Debug)]
38pub struct YSLBBuilder {}
39
40impl YSLBBuilder {
41 pub const fn new() -> Self {
43 YSLBBuilder {}
44 }
45}
46
47impl ScriptBuilder for YSLBBuilder {
48 fn default_encoding(&self) -> Encoding {
49 Encoding::Cp932
50 }
51
52 fn build_script(
53 &self,
54 buf: Vec<u8>,
55 _filename: &str,
56 encoding: Encoding,
57 _archive_encoding: Encoding,
58 config: &ExtraConfig,
59 _archive: Option<&Box<dyn Script>>,
60 ) -> Result<Box<dyn Script + Send + Sync>> {
61 Ok(Box::new(YSLB::new(MemReader::new(buf), encoding, config)?))
62 }
63
64 fn extensions(&self) -> &'static [&'static str] {
65 &["ybn"]
66 }
67
68 fn is_this_format(&self, _filename: &str, buf: &[u8], buf_len: usize) -> Option<u8> {
69 if buf_len >= 4 && buf.starts_with(b"YSLB") {
70 return Some(20);
71 }
72 None
73 }
74
75 fn script_type(&self) -> &'static ScriptType {
76 &ScriptType::YurisYSLB
77 }
78
79 fn can_create_file(&self) -> bool {
80 true
81 }
82
83 fn create_file<'a>(
84 &'a self,
85 filename: &'a str,
86 writer: Box<dyn WriteSeek + 'a>,
87 encoding: Encoding,
88 file_encoding: Encoding,
89 config: &ExtraConfig,
90 ) -> Result<()> {
91 create_file(
92 filename,
93 writer,
94 encoding,
95 file_encoding,
96 config.custom_yaml,
97 )
98 }
99}
100
101#[derive(Debug)]
102pub struct YSLB {
103 pub(super) data: YSLBData,
104 custom_yaml: bool,
105}
106
107impl YSLB {
108 pub fn new<T: Read + Seek>(
109 mut reader: T,
110 encoding: Encoding,
111 config: &ExtraConfig,
112 ) -> Result<Self> {
113 let mut sig = [0; 4];
114 reader.read_exact(&mut sig)?;
115 if &sig != b"YSLB" {
116 anyhow::bail!("Unsupported YSLB file.");
117 }
118 let data = YSLBData::unpack(&mut reader, false, encoding, &None)?;
119 Ok(Self {
120 data,
121 custom_yaml: config.custom_yaml,
122 })
123 }
124}
125
126impl Script for YSLB {
127 fn default_output_script_type(&self) -> OutputScriptType {
128 OutputScriptType::Custom
129 }
130
131 fn is_output_supported(&self, output: OutputScriptType) -> bool {
132 matches!(output, OutputScriptType::Custom)
133 }
134
135 fn default_format_type(&self) -> FormatOptions {
136 FormatOptions::None
137 }
138
139 fn custom_output_extension(&self) -> &'static str {
140 if self.custom_yaml { "yaml" } else { "json" }
141 }
142
143 fn custom_export(&self, filename: &std::path::Path, encoding: Encoding) -> Result<()> {
144 let s = if self.custom_yaml {
145 serde_yaml_ng::to_string(&self.data)
146 .map_err(|e| anyhow::anyhow!("Failed to serialize to YAML: {}", e))?
147 } else {
148 serde_json::to_string_pretty(&self.data)
149 .map_err(|e| anyhow::anyhow!("Failed to serialize to JSON: {}", e))?
150 };
151 let mut writer = crate::utils::files::write_file(filename)?;
152 let s = encode_string(encoding, &s, false)?;
153 writer.write_all(&s)?;
154 writer.flush()?;
155 Ok(())
156 }
157
158 fn custom_import<'a>(
159 &'a self,
160 custom_filename: &'a str,
161 file: Box<dyn WriteSeek + 'a>,
162 encoding: Encoding,
163 output_encoding: Encoding,
164 ) -> Result<()> {
165 create_file(
166 custom_filename,
167 file,
168 encoding,
169 output_encoding,
170 self.custom_yaml,
171 )
172 }
173}
174
175fn create_file<'a>(
176 custom_filename: &'a str,
177 mut writer: Box<dyn WriteSeek + 'a>,
178 encoding: Encoding,
179 output_encoding: Encoding,
180 yaml: bool,
181) -> Result<()> {
182 let input = crate::utils::files::read_file(custom_filename)?;
183 let s = decode_to_string(output_encoding, &input, true)?;
184 let mut data: YSLBData = if yaml {
185 serde_yaml_ng::from_str(&s).map_err(|e| anyhow::anyhow!("Failed to parse YAML: {}", e))?
186 } else {
187 serde_json::from_str(&s).map_err(|e| anyhow::anyhow!("Failed to parse JSON: {}", e))?
188 };
189 data.num_labels = data.labels.len() as u32;
190 data.label_range_start_indexes.resize(0x100, 0);
191 let max = data.num_labels as usize;
192 let mut i = 0;
193 for j in 0..256 {
194 loop {
195 if i >= max {
196 data.label_range_start_indexes[j] = max as u32;
197 break;
198 } else {
199 let lab = data.labels[i].id >> 24;
200 if lab as usize >= j {
201 data.label_range_start_indexes[j] = i as u32;
202 break;
203 }
204 i += 1;
205 }
206 }
207 }
208 writer.write_all(b"YSLB")?;
209 data.pack(&mut writer, false, encoding, &None)?;
210 Ok(())
211}